﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;

namespace Language.Syntax.Colouring
{
    internal class DynamicClassifier : IClassifier
    {
        private struct Classification
        {
            public Span Span { get; set; }
            public IClassificationType Type { get; set; }
        }

        private ITextBuffer _textBuffer;

        public ITextSnapshot TextSnapshot { get; private set; }

        public DynamicClassifier( ITextBuffer textBuffer )
        {
            _textBuffer = textBuffer;
        }

        public IClassificationType this[ Span span ]
        {
            get
            {
                return Classifications.FirstOrDefault( classification => classification.Span == span ).Type;
            }
            set
            {
                if( value != null )
                {
                    Classifications.Add( new Classification() { Span = span, Type = value } );
                }
                else
                {
                    Classifications.RemoveAll( classification => classification.Span == span );
                }

                ClassificationChanged?.Invoke( this, new ClassificationChangedEventArgs( new SnapshotSpan( _textBuffer.CurrentSnapshot, span ) ) );
            }
        }

        private List<Classification> Classifications { get; set; } = new List<Classification>();

        public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;

        public IList<ClassificationSpan> GetClassificationSpans( SnapshotSpan span )
        {
            List<ClassificationSpan> relevantClassifications = new List<ClassificationSpan>();

            foreach ( Classification classification in Classifications )
            {
                if ( span.Contains( classification.Span ) )
                {
                    SnapshotSpan snapshotSpan = new SnapshotSpan( span.Snapshot, classification.Span );
                    relevantClassifications.Add( new ClassificationSpan( snapshotSpan, classification.Type ) );
                }
            }

            return relevantClassifications;
        }
    }

    [Export]
    [Export( typeof( IClassifierProvider ) )]
    [ContentType( "redscript" )]
    internal class DynamicClassificationProvider : IClassifierProvider
    {
        [Import]
        internal IClassificationTypeRegistryService ClassificationRegistry { set { InitializeClassifications( value ); } }
        public IClassificationType ActionGotoDefClassification { get; private set; }
        public DynamicClassifier this[ ITextView view ] => GetDynamicClassifier( view );

        public IClassifier GetClassifier( ITextBuffer textBuffer )
        {
            return textBuffer.Properties.GetOrCreateSingletonProperty( () => new DynamicClassifier( textBuffer ) );
        }

        private void InitializeClassifications( IClassificationTypeRegistryService registryService )
        {
            ActionGotoDefClassification = registryService.GetClassificationType( Classification.Name.ActionGotoDef );
        }
        
        private DynamicClassifier GetDynamicClassifier( ITextView view )
        {
            if ( ActionGotoDefClassification == null )
                return null;
        
            return view.TextBuffer.Properties.GetOrCreateSingletonProperty( () => new DynamicClassifier( view.TextBuffer ) );
        }
    }
}
